{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# SymbolicBayesTreeClique"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "A `SymbolicBayesTreeClique` represents a single node within a `SymbolicBayesTree`. Each clique corresponds to a set of variables (frontal variables) that were eliminated together during the multifrontal elimination process.\n",
        "\n",
        "Key aspects of a clique:\n",
        "*   **Conditional:** It stores the `SymbolicConditional` P(Frontals | Parents/Separator) that results from eliminating the frontal variables.\n",
        "*   **Tree Structure:** It maintains pointers to its parent and children cliques within the Bayes Tree, defining the tree's topology.\n",
        "*   **Frontal and Separator Variables:** Implicitly defines the frontal variables (eliminated in this clique) and separator variables (parents in the Bayes Tree, connecting it to its parent clique).\n",
        "\n",
        "Users typically interact with the `SymbolicBayesTree` as a whole, but understanding the clique structure is helpful for comprehending how the Bayes Tree represents the factored distribution and facilitates efficient inference."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/symbolic/doc/SymbolicBayesTreeClique.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "tags": [
          "remove-cell"
        ]
      },
      "outputs": [],
      "source": [
        "%pip install --quiet gtsam-develop"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [],
      "source": [
        "from gtsam import SymbolicFactorGraph, Ordering\n",
        "# SymbolicBayesTreeClique is accessed *through* a SymbolicBayesTree\n",
        "from gtsam.symbol_shorthand import X, L\n",
        "import graphviz"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Accessing and Inspecting Cliques\n",
        "\n",
        "Cliques are obtained by first creating a `SymbolicBayesTree` (usually via elimination) and then accessing its nodes."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Bayes Tree has 4 cliques.\n",
            "Number of roots: 1\n"
          ]
        },
        {
          "data": {
            "image/svg+xml": [
              "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
              "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
              " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
              "<!-- Generated by graphviz version 12.0.0 (0)\n",
              " -->\n",
              "<!-- Title: G Pages: 1 -->\n",
              "<svg width=\"170pt\" height=\"188pt\"\n",
              " viewBox=\"0.00 0.00 169.99 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
              "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n",
              "<title>G</title>\n",
              "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-184 165.99,-184 165.99,4 -4,4\"/>\n",
              "<!-- 6 -->\n",
              "<g id=\"node1\" class=\"node\">\n",
              "<title>6</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"82.02\" cy=\"-162\" rx=\"34.46\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"82.02\" y=\"-156.95\" font-family=\"Times,serif\" font-size=\"14.00\">x1, x2</text>\n",
              "</g>\n",
              "<!-- 7 -->\n",
              "<g id=\"node2\" class=\"node\">\n",
              "<title>7</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"37.02\" cy=\"-90\" rx=\"37.02\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"37.02\" y=\"-84.95\" font-family=\"Times,serif\" font-size=\"14.00\">x0 : x1</text>\n",
              "</g>\n",
              "<!-- 6&#45;&gt;7 -->\n",
              "<g id=\"edge1\" class=\"edge\">\n",
              "<title>6&#45;&gt;7</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M71.36,-144.41C66.09,-136.22 59.61,-126.14 53.71,-116.95\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"56.78,-115.26 48.43,-108.74 50.89,-119.05 56.78,-115.26\"/>\n",
              "</g>\n",
              "<!-- 9 -->\n",
              "<g id=\"node4\" class=\"node\">\n",
              "<title>9</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"127.02\" cy=\"-90\" rx=\"34.97\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"127.02\" y=\"-84.95\" font-family=\"Times,serif\" font-size=\"14.00\">l2 : x1</text>\n",
              "</g>\n",
              "<!-- 6&#45;&gt;9 -->\n",
              "<g id=\"edge3\" class=\"edge\">\n",
              "<title>6&#45;&gt;9</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M92.69,-144.41C97.95,-136.22 104.43,-126.14 110.34,-116.95\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"113.15,-119.05 115.61,-108.74 107.26,-115.26 113.15,-119.05\"/>\n",
              "</g>\n",
              "<!-- 8 -->\n",
              "<g id=\"node3\" class=\"node\">\n",
              "<title>8</title>\n",
              "<ellipse fill=\"none\" stroke=\"black\" cx=\"37.02\" cy=\"-18\" rx=\"34.97\" ry=\"18\"/>\n",
              "<text text-anchor=\"middle\" x=\"37.02\" y=\"-12.95\" font-family=\"Times,serif\" font-size=\"14.00\">l1 : x0</text>\n",
              "</g>\n",
              "<!-- 7&#45;&gt;8 -->\n",
              "<g id=\"edge2\" class=\"edge\">\n",
              "<title>7&#45;&gt;8</title>\n",
              "<path fill=\"none\" stroke=\"black\" d=\"M37.02,-71.7C37.02,-64.41 37.02,-55.73 37.02,-47.54\"/>\n",
              "<polygon fill=\"black\" stroke=\"black\" points=\"40.52,-47.62 37.02,-37.62 33.52,-47.62 40.52,-47.62\"/>\n",
              "</g>\n",
              "</g>\n",
              "</svg>\n"
            ],
            "text/plain": [
              "<graphviz.sources.Source at 0x105c27740>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Create a factor graph\n",
        "graph = SymbolicFactorGraph()\n",
        "graph.push_factor(X(0))\n",
        "graph.push_factor(X(0), X(1))\n",
        "graph.push_factor(X(1), X(2))\n",
        "graph.push_factor(X(0), L(1))\n",
        "graph.push_factor(X(1), L(2))\n",
        "\n",
        "# Eliminate to get a Bayes Tree\n",
        "ordering = Ordering.ColamdSymbolicFactorGraph(graph)\n",
        "bayes_tree = graph.eliminateMultifrontal(ordering)\n",
        "\n",
        "print(f\"Bayes Tree has {bayes_tree.size()} cliques.\")\n",
        "\n",
        "roots = bayes_tree.roots()\n",
        "print(f\"Number of roots: {len(roots)}\")\n",
        "\n",
        "# Visualize the Bayes tree using graphviz\n",
        "display(graphviz.Source(bayes_tree.dot()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "Inspecting Clique 0:\n",
            "  Clique Structure:  P( x1 x2)\n",
            "  Associated Conditional: P( P( x1 x2)\n",
            "  Is Root? True\n",
            "  Parent Clique is None (likely a root clique).\n",
            "  Number of Children: 2\n",
            "\n",
            "Inspecting Clique 0:\n",
            "  Clique Structure:  P( x0 | x1)\n",
            "  Associated Conditional: P( P( x0 | x1)\n",
            "  Is Root? False\n",
            "  Parent Clique exists.\n",
            "  Number of Children: 1\n",
            "\n",
            "Inspecting Clique 0:\n",
            "  Clique Structure:  P( l1 | x0)\n",
            "  Associated Conditional: P( P( l1 | x0)\n",
            "  Is Root? False\n",
            "  Parent Clique exists.\n",
            "  Number of Children: 0\n",
            "\n",
            "Inspecting Clique 0:\n",
            "  Clique Structure:  P( l2 | x1)\n",
            "  Associated Conditional: P( P( l2 | x1)\n",
            "  Is Root? False\n",
            "  Parent Clique exists.\n",
            "  Number of Children: 0\n"
          ]
        }
      ],
      "source": [
        "def inspect(clique):\n",
        "    print(\"\\nInspecting Clique 0:\")\n",
        "    clique.print(\"  Clique Structure: \")\n",
        "    \n",
        "    # Get the conditional stored in the clique\n",
        "    conditional = clique.conditional()\n",
        "    if conditional:\n",
        "        conditional.print(\"  Associated Conditional: P(\")\n",
        "    else:\n",
        "        print(\"  Clique has no associated conditional (might be empty root)\")\n",
        "        \n",
        "    # Check properties\n",
        "    print(f\"  Is Root? {clique.isRoot()}\")\n",
        "    # Accessing parent/children is possible in C++ but might be less direct or typical in Python wrapper usage\n",
        "    # Parent clique (careful, might be null for root)\n",
        "    parent_clique = clique.parent() \n",
        "    if parent_clique:\n",
        "        print(\"  Parent Clique exists.\")\n",
        "    else:\n",
        "        print(\"  Parent Clique is None (likely a root clique).\")\n",
        "        \n",
        "    print(f\"  Number of Children: {clique.nrChildren()}\") # Example if method existed\n",
        "\n",
        "def traverse_clique(clique):\n",
        "    inspect(clique)\n",
        "    for j in range(clique.nrChildren()):\n",
        "        traverse_clique(clique[j])\n",
        "\n",
        "for root in roots:\n",
        "    traverse_clique(root)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "py312",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}
